import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
// org.antlr.v4.runtime.tree.ParseTree
import org.antlr.v4.runtime.tree.TerminalNode;


import models.types;


public class VisitHandler_index extends GoParserBaseVisitor<Integer> {

    enum Type{
        ID, 
        INT, FLOAT,
        PLUS, MUL, SUB, DIV
    }

    public class Log{
        public Type type;
        public String name;
        public int args[] = {-1, -1};
        public int int_val;
        public float float_val;
        Log(Type type, String name, int arg1, int arg2, int int_val, float float_val){
            this.type = type;
            this.name = name;
            this.args[0] = arg1;
            this.args[1] = arg2;
            this.int_val = int_val;
            this.float_val = float_val;
        }
        // constructor for identifier log (declaration without assignment)
        Log(Type type, String name){
            this.type = type;
            this.name = name;
        }

        // constructor for multiple operation
        Log(Type type){
            this.type = type;
        }

        // constructor for integer
        Log(Type type, int value){
            this.type = type;
            this.int_val = value;
        }

        // constructor for float
        Log(Type type, float value){
            this.type = type;
            this.float_val = value;
        }

        @Override
        public String toString() {
            String res = String.format(
                "[type]=%s, [name]=%s, [arg1]=%d, [arg2]=%d, [int_val]=%d, [float_val]=%f\n", 
                type.toString(), name, args[0], args[1], int_val, float_val
            );
            return res;
        }
    }

    public class Pair{
        int row; // the row index
        int col; // 0 or 1 --- arg1 or arg2
        Pair(int row, int col){
            this.row = row;
            this.col = col;
        }
        Pair(int row){
            this.row = row;
            this.col = 0;
        }
    }

    public Stack<Pair> stk = new Stack<>();
    public ArrayList<Log> table = new ArrayList<>();

    public void printTable(){
        System.out.println("---------------------------- Table ----------------------------");
        for (int i = 0; i < table.size(); i ++){
            Log line = table.get(i);
            System.out.println(line);;
        }
        System.out.println("---------------------------------------------------------------");
    }

    @Override
    public Integer visitShortVarDecl(GoParser.ShortVarDeclContext ctx) {  
        System.out.println("---------------------------- visitShortDecl ----------------------------");   
        System.out.println(ctx.getText());   
        List<TerminalNode> idList = ctx.identifierList().IDENTIFIER();
        for (int i = idList.size() - 1; i >= 0; i --){
            TerminalNode id = idList.get(i);
            Log log = new Log(Type.ID, id.getText());
            stk.push(new Pair(table.size()));
            table.add(log);            
        }
        return super.visitShortVarDecl(ctx);
    }

    @Override
    public Integer visitExpression(GoParser.ExpressionContext ctx) {
        Pair blank = stk.pop();
        int row = blank.row;
        int col = blank.col;
        System.out.println(String.format("(%d, %d)", row, col));
        
        // create a log for this expression
        if (ctx.primaryExpr() != null){
            if (ctx.primaryExpr().operand() != null
                && ctx.primaryExpr().operand().literal() != null
                && ctx.primaryExpr().operand().literal().basicLit() != null
                && ctx.primaryExpr().operand().literal().basicLit().integer() != null){
                    GoParser.IntegerContext int_ctx = ctx.primaryExpr().operand().literal().basicLit().integer();
                    TerminalNode intNode = int_ctx.DECIMAL_LIT();
                    Log tmp = new Log(Type.INT, Integer.parseInt(intNode.getText()));
                    table.add(tmp);
                }
        }
        else if(ctx.add_op != null){ // PLUS | MINUS | OR | CARET
            if(ctx.PLUS() != null){
                Log tmp = new Log(Type.PLUS);
                stk.push(new Pair(table.size(), 1));
                stk.push(new Pair(table.size(), 0));
            }
        }

        // fill the blank
        table.get(row).args[col] = table.size();
        System.out.println("getargs");
        return super.visitExpression(ctx);
    }

    @Override
    public Integer visitStatement(GoParser.StatementContext ctx) {
        printTable();
        // TODO Auto-generated method stub
        return super.visitStatement(ctx);
    }
}
